﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.Win32;
using System.Security;

namespace RoketPack
{
    class RecoveryUtil
    {
        private static string p_RecoveryDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\RoketPackErrorData.dat";

        internal class RecoveryData
        {
            internal bool CausedByException;
            internal string ExceptionType;
            internal string ExceptionMessage;
            internal string ExceptionSource;
            internal string ExceptionHelpLink;
            internal string ExceptionStackTrace;
            internal string RecoveryLog;
            internal string[] Repositories = new string[] { };
        }

        internal static string RecoveryDataPath
        {
            get
            {
                return RecoveryUtil.p_RecoveryDataPath;
            }
        }

        internal static bool IsRequired()
        {
            return (File.Exists(p_RecoveryDataPath));
        }

        internal static bool Run()
        {
            RecoveryWizard s = new RecoveryWizard();
            s.RecoveryData = RecoveryUtil.ReadRecoveryData();
            if (s.RecoveryData == null)
            {
                s.RecoveryData = new RecoveryUtil.RecoveryData();
                s.RecoveryData.CausedByException = false;
            }
            if (s.ShowDialog() == DialogResult.Cancel)
                return false;
            else
                return true;
        }

        private static RecoveryData ReadRecoveryData()
        {
            try
            {
                using (StreamReader reader = new StreamReader(RecoveryUtil.p_RecoveryDataPath))
                {
                    RecoveryData r = new RecoveryData();
                    string[] data = reader.ReadToEnd().Split('\0');
                    r.CausedByException = true;
                    r.ExceptionType = data[0];
                    r.ExceptionMessage = data[1];
                    r.ExceptionSource = data[2];
                    r.ExceptionHelpLink = data[3];
                    r.ExceptionStackTrace = data[4];
                    return r;
                }
            }
            catch (Exception)
            {
                return null;
            }
        }

        internal static bool SaveRecoveryData(Exception ex)
        {
            try
            {
                using (StreamWriter writer = new StreamWriter(RecoveryUtil.p_RecoveryDataPath))
                {
                    writer.Write(ex.GetType().ToString() + "\0");
                    writer.Write(ex.Message + "\0");
                    writer.Write(ex.Source + "\0");
                    writer.Write(ex.HelpLink + "\0");
                    writer.Write(ex.StackTrace + "\0");
                    return true;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

        internal static void RegenerateRepositoryXml()
        {
            using (StreamWriter writer = new StreamWriter("Repositories.xml", false))
            {
                string data = "";
                data += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";
                data += "<repositories>\r\n";
                data += "  <repo url=\"https://pack.roket-enterprises.com\">\r\n";
                data += "    <name>Official Software Repository</name>\r\n";
                data += "    <description>The official software repository for software written or endorsed by Roket Enterprises.</description>\r\n";
                data += "  </repo>\r\n";
                data += "  <repo url=\"https://pack.roket-enterprises.com/experimental/\">\r\n";
                data += "    <name>Experimental Software Repository</name>\r\n";
                data += "    <description>A repository which provides experimental and unstable software.</description>\r\n";
                data += "  </repo>\r\n";
                data += "</repositories>\r\n";
                writer.Write(data);
                writer.Close();
            }
        }

        internal static bool RecoverVersionInformation(RoketPack.Management.Repository r,
            RoketPack.Management.Application a,
            RoketPack.Management.Component c,
            RoketPack.Management.Objects.File f)
        {
            int version = GetVersionInformationForObject(r.Url, a.ID, c.ID, "file", f.Local);
            if (version == -1) return false;
            f.InstalledVersion = version;
            return true;
        }

        internal static bool RecoverVersionInformation(RoketPack.Management.Repository r,
            RoketPack.Management.Application a,
            RoketPack.Management.Component c,
            RoketPack.Management.Objects.Folder f)
        {
            int version = GetVersionInformationForObject(r.Url, a.ID, c.ID, "folder", f.Local);
            if (version == -1) return false;
            f.InstalledVersion = version;
            return true;
        }

        internal static bool RecoverVersionInformation(RoketPack.Management.Repository r,
            RoketPack.Management.Application a,
            RoketPack.Management.Component c,
            RoketPack.Management.Objects.Link f)
        {
            int version = GetVersionInformationForObject(r.Url, a.ID, c.ID, "link", f.Local);
            if (version == -1) return false;
            f.InstalledVersion = version;
            return true;
        }

        internal static bool RecoverVersionInformation(RoketPack.Management.Repository r,
            RoketPack.Management.Application a,
            RoketPack.Management.Component c,
            RoketPack.Management.Objects.Classic classic)
        {
            int version = GetVersionInformationForObject(r.Url, a.ID, c.ID, "classic", "installer");
            if (version == -1) return false;
            classic.InstalledVersion = version;
            return true;
        }

        internal static int GetVersionInformationForObject(string repourl, string appid, string comid, string type, string path)
        {
            try
            {
                RegistryKey r = Registry.CurrentUser;
                r = r.OpenSubKey("Software", RegistryKeyPermissionCheck.ReadSubTree);
                r = r.OpenSubKey("Roket Enterprises");
                r = r.OpenSubKey("RoketPack");
                r = r.OpenSubKey("Versioning");
                r = r.OpenSubKey(repourl);
                r = r.OpenSubKey(appid);
                r = r.OpenSubKey(comid);
                if (r.GetValueKind(type + ":" + path) == RegistryValueKind.DWord)
                    return (int)r.GetValue(type + ":" + path);
                else
                    return -1;
            }
            catch (SecurityException)
            {
                return -1;
            }
            catch (UnauthorizedAccessException)
            {
                return -1;
            }
            catch (IOException)
            {
                return -1;
            }
            catch (InvalidCastException)
            {
                return -1;
            }
            catch (ObjectDisposedException)
            {
                return -1;
            }
            catch (NullReferenceException)
            {
                return -1;
            }
        }

        internal static bool SaveVersionInformationForObject(string repourl, string appid, string comid, string type, string path, int version)
        {
            try
            {
                RegistryKey r = Registry.CurrentUser;
                r = r.CreateSubKey("Software", RegistryKeyPermissionCheck.ReadWriteSubTree);
                r = r.CreateSubKey("Roket Enterprises");
                r = r.CreateSubKey("RoketPack");
                r = r.CreateSubKey("Versioning");
                r = r.CreateSubKey(repourl);
                r = r.CreateSubKey(appid);
                r = r.CreateSubKey(comid);
                r.SetValue(type + ":" + path, version);
                return true;
            }
            catch (SecurityException)
            {
                return false;
            }
            catch (UnauthorizedAccessException)
            {
                return false;
            }
            catch (IOException)
            {
                return false;
            }
            catch (InvalidCastException)
            {
                return false;
            }
            catch (ObjectDisposedException)
            {
                return false;
            }
            catch (NullReferenceException)
            {
                return false;
            }
        }
    }
}
